package org.fhm.zdte.common.service.api;

import org.fhm.substrate.annotation.Component;
import org.fhm.substrate.service.LoggerHandler;
import org.fhm.substrate.standard.ILogger;
import org.fhm.zdte.common.exception.ProtocolException;
import org.fhm.zdte.common.standard.IThreadManager;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Common thread manager.
 *
 * @author 谭波
 * @since 2023/11/29
 */
@Component
public class CommonThreadManager implements IThreadManager {

    public static final String ONLINE_CLIENT_CONDITION = "ONLINE_CLIENT_CONDITION";

    public static final String CONNECT_CONDITION = "CONNECT_CONDITION_";

    private final Map<String, ReentrantLock> lockMap = new ConcurrentHashMap<>();

    private final Map<String, Condition> conditionMap = new ConcurrentHashMap<>();

    private final ILogger logger = LoggerHandler.getLogger(CommonThreadManager.class);

    @Override
    public boolean waitForTime(int time, TimeUnit unit, String name) throws ProtocolException {
        ReentrantLock lock;
        Condition condition;
        synchronized (this) {
            lock = new ReentrantLock();
            condition = lock.newCondition();
            if (Objects.nonNull(name)) {
                if (Objects.nonNull(lockMap.get(name)) || Objects.nonNull(conditionMap.get(name)))
                    throw new ProtocolException("do not call the same API at the same time");
                lockMap.put(name, lock);
                conditionMap.put(name, condition);
            }
        }
        lock.lock();
        try {
            return condition.await(time, unit);
        } catch (InterruptedException e) {
            logger.error(e, e.getMessage());
            return true;
        } finally {
            if (lock.isLocked()) {
                lock.unlock();
            }
            if (Objects.nonNull(name)) {
                lockMap.remove(name);
                conditionMap.remove(name);
            }
        }
    }

    @Override
    public boolean notify(String name) {
        if (Objects.nonNull(lockMap.get(name)) && Objects.nonNull(conditionMap.get(name))) {
            synchronized (this) {
                ReentrantLock lock;
                Condition condition;
                if (
                        Objects.nonNull((lock = lockMap.get(name)))
                                && Objects.nonNull((condition = conditionMap.get(name)))
                ) {
                    lock.lock();
                    try {
                        condition.signal();
                    } catch (Exception e) {
                        logger.error(e, "awake fail");
                    } finally {
                        lock.unlock();
                    }
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public void notifyAllLock() {
        lockMap.keySet().forEach(this::notify);
    }
}
